﻿namespace JoinBox.CAD;

using System;

public static class AutoGo
{
    /// <summary>
    /// Acap上下文_文档窗口管理器
    /// </summary>
    public static SyncEx AcapMidSyncEx
    {
        get
        {
            if (_AcapMidSyncContext == null)
            {
                var dm = Acap.DocumentManager;
                var doc = dm.MdiActiveDocument;
                if (doc == null)
                    throw new ArgumentNullException("无法处理的错误::" + nameof(AcapMidSyncEx));
                _AcapMidSyncContext = new(WindowsAPI.GetParent(doc.Window.Handle));
            }
            return _AcapMidSyncContext;
        }
    }
    static SyncEx? _AcapMidSyncContext;

    /// <summary>
    /// Acap上下文_主窗口
    /// </summary>
    public static SyncEx AcapMainSyncEx => _AcapMainSyncEx ??= new(Acap.MainWindow.Handle);
    static SyncEx? _AcapMainSyncEx;

    /// <summary>
    /// 在其他线程Post异步发送到cad主线程(Send无法响应鼠标)<br/>
    /// 不要在cad主线程Post否则卡死
    /// </summary>
    /// <param name="action"></param>
    public static void Post(Action action)
    {
        AcapMainSyncEx.Post(action);
    }

    public static void Printl(this object str)
    {
        AcapMainSyncEx.Post(() => {
            var doc = Acap.DocumentManager.MdiActiveDocument;
            if (doc is null)
                return;
            doc.Editor.WriteMessage(Environment.NewLine + str.ToString());
        });
        Debug.WriteLine(str);
    }


    /// <summary>
    /// 创建线程进行循环等待
    /// </summary>
    /// <param name="awaitActiveDocument">等候当前文档不为空的时候执行</param>
    /// <param name="runTask">所有等候完成之后的执行任务</param>
    /// <param name="awaitTask">需要补充的等候任务;它将会在<paramref name="awaitActiveDocument"/>之后进行<br/>
    /// 返回<see langword="false"/>就继续等待</param>
    /// <param name="state">线程方式</param>
    /// <returns>返回创建的线程,用于释放</returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static Thread AcapMainAwait(bool awaitActiveDocument,
                                       Action runTask,
                                       Func<bool>? awaitTask = null,
                                       ApartmentState state = ApartmentState.STA/*WPF需要此类型*/)
    {
        if (runTask == null)
            throw new ArgumentNullException(nameof(runTask));

        var thread = new Thread(() => {
            var breakFlag = false;
            while (true)
            {
                /// 直到 <see cref="IExtensionApplication"/> 完成,此处才会通过
                if (!Acap.IsQuiescent)
                {
                    Thread.Sleep(200);
                    continue;
                }

                /// 如果这里一直是null,表示 <see cref="SyncEx.WndProc"/> 没有实现过一次
                /// 那么就需要在构造函数去默认实现一次
                if (!AcapMainSyncEx.ExistContextCache)
                {
                    RefreshPos();
                    Thread.Sleep(200);
                    continue;
                }
                if (awaitActiveDocument)
                {
                    // Send(切换到主线程) 才能立即获取所需要的值
                    AcapMainSyncEx.Send(() => {
                        var dm = Acap.DocumentManager;
                        var doc = dm.MdiActiveDocument;
                        if (doc != null)
                            breakFlag = true;
                    });
                    if (!breakFlag)
                        RefreshPos();
                }
                else
                {
                    breakFlag = true;
                }

                if (breakFlag && awaitTask != null)
                    breakFlag = awaitTask.Invoke();
                if (breakFlag)
                    break;
                //Debug.WriteLine(DateTime.Now);
                Thread.Sleep(200);
            }
            runTask.Invoke();
        });
        thread.SetApartmentState(state);
        thread.Start();
        return thread;
    }

    /// <summary>
    /// 触发刷新,避免卡死<br/>
    /// 需要在其他线程中实现,若先利用 Send/Post (切到cad主线程) 会导致卡死;<br/>
    /// 0x01 注册表自动load的时候,鼠标在绘图区中间或者界面外,<br/>
    /// 0x02 netload命令的时候,程序界面已经完成,<br/>
    /// 这两种情况,等不到当前上下文切换,上下文缓存会一直为null<br/>
    /// 这句就是触发当前上下文切换<br/>
    /// 0x03 当上下文缓存不为null,那么当前上下文是null,也需要触发,否则获取当前文档是null
    /// </summary>
    public static void RefreshPos()
    {
        WindowsAPI.SetWindowPos(AcapMainSyncEx.Handle);
    }
}